<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>xeokit Example</title>
    <link href="../css/pageStyle.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script>

</head>
<body>
<input type="checkbox" id="info-button"/>
<label for="info-button" class="info-button"><i class="far fa-3x fa-question-circle"></i></label>
<canvas id="myCanvas"></canvas>
<div class="slideout-sidebar">
    <h1>Cursor Snapping</h1>
    <p>Cursor snapping with a SceneModel in data texture mode with batched geometries</p>
    <p>Hover over model to snap pointer to the nearest vertex or edge</p>
    <h3>Components Used</h3>
    <ul>
        <li>
            <a href="../../docs/class/src/viewer/Viewer.js~Viewer.html"
               target="_other">Viewer</a>
        </li>
        <li>
            <a href="../../docs/class/src/plugins/XKTLoaderPlugin/XKTLoaderPlugin.js~XKTLoaderPlugin.html"
               target="_other">XKTLoaderPlugin</a>
        </li>
    </ul>
</div>
</body>
<script type="module">

    //------------------------------------------------------------------------------------------------------------------
    // Import the modules we need for this example
    //------------------------------------------------------------------------------------------------------------------

    import {Viewer, buildSphereGeometry,
        buildBoxGeometry,
        buildCylinderGeometry,
        buildTorusGeometry,
        buildBoxLinesGeometry,
        buildGridGeometry,
        buildVectorTextGeometry,
        SceneModel
    } from "../../dist/xeokit-sdk.min.es.js";

    //------------------------------------------------------------------------------------------------------------------
    // Create a Viewer and arrange the camera
    //------------------------------------------------------------------------------------------------------------------

    const viewer = new Viewer({
        canvasId: "myCanvas",
        dtxEnabled: true
    });

    viewer.scene.camera.eye = [-10.970825516832445, 12.854221661021146, 24.950601235099196];
    viewer.scene.camera.look = [8.500572204589844, 2.190000057220459, 0];
    viewer.scene.camera.up = [0.18, 0.93, -0.25];

    viewer.cameraControl.navMode = "orbit";
    viewer.cameraControl.followPointer = true;

    viewer.scene.pointsMaterial.roundPoints = true;
    viewer.scene.pointsMaterial.perspectivePoints = true;
    viewer.scene.pointsMaterial.pointSize = 2;
    viewer.scene.pointsMaterial.minPerspectivePointSize = 2;
    viewer.scene.pointsMaterial.maxPerspectivePointSize = 6;

    viewer.scene.linesMaterial.lineWidth = 1;

    //------------------------------------------------------------------------------------------------------------------
    // Create a SceneModel containing an assortment of the available geometry primitive types
    //------------------------------------------------------------------------------------------------------------------

    const sceneModel = new SceneModel(viewer.scene, {
        id: "myGeometries"
    });

    // Box triangles

    const box = buildBoxGeometry({
        xSize: 1,
        ySize: 1,
        zSize: 1
    });


    sceneModel.createMesh({
        id: "boxMesh",
        primitive: "triangles",
        positions: box.positions,
        normals: box.normals,
        indices: box.indices,
        position: [0, 0, 0],
        color: [1, 0, 0]
    });

    // Box lines

    const boxLines = buildBoxLinesGeometry({
        xSize: 1,
        ySize: 1,
        zSize: 1
    });

    sceneModel.createMesh({
        id: "boxLinesMesh",
        primitive: "lines",
        positions: boxLines.positions,
        indices: boxLines.indices,
        position: [3, 0, 0],
        color: [0, 0, 1]
    });

    // Sphere triangles

    const sphere = buildSphereGeometry({
        center: [0, 0, 0],
        radius: 1.5,
        heightSegments: 60,
        widthSegments: 60
    });

    sceneModel.createMesh({
        id: "sphereMesh",
        primitive: "triangles",
        positions: sphere.positions,
        normals: sphere.normals,
        indices: sphere.indices,
        position: [7, 0, 0],
        color: [0, 0.5, 1]
    });

    // Torus triangles

    const torus = buildTorusGeometry({
        center: [0, 0, 0],
        radius: 1.5,
        tube: 0.5,
        radialSegments: 32,
        tubeSegments: 24,
        arc: Math.PI * 2.0
    });

    sceneModel.createMesh({
        id: "torusMesh",
        primitive: "triangles",
        positions: torus.positions,
        normals: torus.normals,
        indices: torus.indices,
        position: [11, 0, 0],
        color: [0.7, 0, 1]
    });

    // Cylinder triangles

    const cylinder = buildCylinderGeometry({
        center: [0, 0, 0],
        radiusTop: 1.0,
        radiusBottom: 2.0,
        height: 2.5,
        radialSegments: 20,
        heightSegments: 1,
        openEnded: false
    });

    sceneModel.createMesh({
        id: "cylinderMesh",
        primitive: "triangles",
        positions: cylinder.positions,
        normals: cylinder.normals,
        indices: cylinder.indices,
        position: [16, 0, 0],
        color: [1, .6, 0]
    });

    // Grid lines

    const grid = buildGridGeometry({
        size: 10,
        divisions: 10
    });

    sceneModel.createMesh({
        id: "gridMesh",
        primitive: "lines",
        positions: grid.positions,
        indices: grid.indices,
        position: [25, 0, 0],
        color: [0, 1, 0]
    });

    // Text

    const text = buildVectorTextGeometry({
        origin: [0, 0, 0],
        text: "Mouse over this text and these objects\nto snap cursor to lines and points\n(data texture scene SceneModel w/batching)"
    });

    sceneModel.createMesh({
        id: "textMesh",
        primitive: "lines",
        positions: text.positions,
        indices: text.indices,
        position: [0, 7.5, 0],
        color: [0, 0, 0]
    });

    // Points

    const positions = [];
    const colors = [];

    const map = {};

    for (let i = 0; i < 2000;) {

        const x = Math.random();
        const y = Math.random();
        const z = Math.random();

        const hash = "" + x + "." + y + "." + z;
        if (map[hash]) {
            continue;
        }

        map[hash] = true;

        positions.push(x);
        positions.push(y);
        positions.push(z);

        colors.push(Math.random());
        colors.push(Math.random());
        colors.push(Math.random());
        colors.push(Math.random());

        i++;
    }

    sceneModel.createMesh({
        id: "pointsMesh",
        primitive: "points",
        positions,
        colors,
        position: [-7, 0, 0],
        scale: [4, 4, 4],
        rotation: [0, 0, 0]
    });

    // One entity contains all our meshes

    sceneModel.createEntity({
        id: "geometries",
        meshIds: ["boxMesh", "boxLinesMesh", "sphereMesh", "torusMesh", "cylinderMesh", "gridMesh", "textMesh", "pointsMesh"]
    });

    sceneModel.finalize();

    //------------------------------------------------------------------------------------------------------------------
    // Mouse hover to snap to vertices and edges
    //------------------------------------------------------------------------------------------------------------------

    viewer.scene.highlightMaterial.fill = false;
    viewer.scene.highlightMaterial.edgeAlpha = 0.5;

    const markerDiv = document.createElement('div');
    const canvas = viewer.scene.canvas.canvas;
    canvas.parentNode.insertBefore(markerDiv, canvas);

    markerDiv.style.background = "black";
    markerDiv.style.border = "2px solid blue";
    markerDiv.style.borderRadius = "20px";
    markerDiv.style.width = "10px";
    markerDiv.style.height = "10px";
    markerDiv.style.margin = "-200px -200px";
    markerDiv.style.zIndex = "100";
    markerDiv.style.position = "absolute";
    markerDiv.style.pointerEvents = "none";

    // Mouse input

    var lastEntity = null;

    function updateCursorPosition(canvasPos) {

        const pickResult = viewer.scene.pick({
            canvasPos,
            snapRadius: 30,
            snapToEdge: true, // Default is true
            snapToVertex: true // Default is true
        });

        if (pickResult && pickResult.snappedCanvasPos) {
            markerDiv.style.marginLeft = `${pickResult.snappedCanvasPos[0] - 10}px`;
            markerDiv.style.marginTop = `${pickResult.snappedCanvasPos[1] - 10}px`;
            markerDiv.style.background = "greenyellow";
            markerDiv.style.border = "3px solid green";

            if (!lastEntity || (pickResult.entity && pickResult.entity.id !== lastEntity.id)) {
                if (lastEntity) {
                    lastEntity.highlighted = false;
                }
                lastEntity = pickResult.entity;
                if (pickResult.entity) {
                    pickResult.entity.highlighted = true;
                }
            }
        } else {
            markerDiv.style.marginLeft = `${canvasPos[0] - 10}px`;
            markerDiv.style.marginTop = `${canvasPos[1] - 10}px`;
            markerDiv.style.background = "white";
            markerDiv.style.border = "1px solid black";

            if (lastEntity) {
                lastEntity.highlighted = false;
                lastEntity = null;
            }
        }
    }

    function mouseUpdateCursorPosition(event) {
        event.preventDefault();
        updateCursorPosition([event.clientX, event.clientY]);
    }

    function touchUpdateCursorPosition(event) {
        event.preventDefault();
        const touch = event.touches[0];
        updateCursorPosition([touch.clientX, touch.clientY]);
    }

    document.addEventListener("mousemove", mouseUpdateCursorPosition);
    document.addEventListener("touchstart", touchUpdateCursorPosition);
    document.addEventListener("touchmove", touchUpdateCursorPosition);

</script>
</html>